package dsx.netServer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.util.ArrayList;

import dsx.config.Config;
import dsx.config.ConfigItem;
import dsx.console.DsxConsole;
import dsx.inf.IDataTransfer;
import dsx.inf.INetServer;
import dsx.inf.ISlicer;
import dsx.log.ServLog;

public class TCPServer implements INetServer {
	private boolean running = false;
	private ServerBootstrap bootstrap = null;
	private EventLoopGroup bossGroup = null;
	private EventLoopGroup workerGroup = null;
	private ChannelFuture channelF = null;
	private TCPChannelTable channelT = null;
	private _TCPConfig tcpConfig = null;

	private String serverName = "";
	IDataTransfer dataTransfer = null;
	ISlicer slicer = null;

	@Override
	public void init(String serverName, IDataTransfer dataTransfer,
			ISlicer slicer) {
		synchronized (this) {
			this.serverName = serverName;
			this.dataTransfer = dataTransfer;
			this.slicer = slicer;
			this.tcpConfig = loadConfig(); // 预读参数，在服务未启动时，可以被上层调用读取到参数
		}
	}

	@Override
	public boolean start() {
		synchronized (this) {
			if (this.running)
				return true;
			try {
				this.bootstrap = new ServerBootstrap();
				this.bossGroup = new NioEventLoopGroup();
				this.workerGroup = new NioEventLoopGroup();
				this.channelT = new TCPChannelTable(
						this.tcpConfig.getMaxConNum(),
						this.tcpConfig.getTimeout());
				this.bootstrap
						.group(this.bossGroup, this.workerGroup)
						.channel(NioServerSocketChannel.class)
						.childHandler(
								new TCPChannelInitializer(this.dataTransfer,
										this.slicer, this.tcpConfig,
										this.channelT))
						.option(ChannelOption.SO_BACKLOG, 128)
						.childOption(ChannelOption.SO_KEEPALIVE, true);
				this.bootstrap.bind(this.tcpConfig.getPort());
				this.channelT.start();
				this.running = true;

				DsxConsole.out(getConfig(), "net tcp server start",
						ServLog.info);
				return true;
			} catch (Exception ex) {
				DsxConsole.out(ex.getMessage() + "." + getConfig(),
						"net tcp server start", ServLog.error);
				return false;
			}
		}
	}

	@Override
	public void stop() {
		synchronized (this) {
			if (!this.running)
				return;
			else {
				try {
					this.channelT.stopEx();
					this.channelF.channel().close().sync();
					this.channelF.channel().closeFuture().sync();
					this.running = false;
				} catch (Exception ex) {
					DsxConsole.out(ex.getMessage(), "net tcp server stop",
							ServLog.error);
				} finally {
					this.workerGroup.shutdownGracefully();
					this.bossGroup.shutdownGracefully();
				}
			}
		}
	}

	@Override
	public boolean send(Object pipe, byte[] answ) {
		synchronized (this) {
			try {
				ChannelHandlerContext ctx = (ChannelHandlerContext) pipe;
				if (ctx != null && answ != null) {
					ByteBuf outMsg = ctx.alloc().buffer(answ.length);
					outMsg.writeBytes(answ);
					ctx.writeAndFlush(outMsg);
					return true;
				}
			} catch (Exception ex) {
				DsxConsole.out(ex.getMessage(), "net tcp server send",
						ServLog.error);
				throw ex;
			}

		}
		return false;
	}

	@Override
	public int getStatus() {
		if (running)
			return 1;
		else
			return 0;
	}

	@Override
	public String getConfig() {
		synchronized (this) {
			if (tcpConfig != null)
				return String
						.format("protocol:tcp; port:%d;maxConNum:%d;timeout:%ds;maxBufSize:%d;initBufferSize:%d",
								tcpConfig.getPort(), tcpConfig.getMaxConNum(),
								tcpConfig.getTimeout(),
								tcpConfig.getMaxBufSize(),
								tcpConfig.getInitBufSize());
			else
				return null;
		}
	}

	private _TCPConfig loadConfig() {
		_TCPConfig tcpConfig = new _TCPConfig();
		ArrayList<ConfigItem> configItems = Config.get(this.serverName);
		for (int i = 0; i < configItems.size(); i++) {
			switch (configItems.get(i).name) {
			case "port":
				tcpConfig.setPort(Integer.parseInt(configItems.get(i).value));
				break;
			case "maxBufSize":
				tcpConfig
						.setMaxBufSize(Integer.parseInt(configItems.get(i).value));
				break;
			case "initBufSize":
				tcpConfig
						.setInitBufSize(Integer.parseInt(configItems.get(i).value));
				break;
			case "timeout":
				tcpConfig
						.setTimeout(Integer.parseInt(configItems.get(i).value));
				break;
			case "maxConNum":
				tcpConfig
						.setMaxConNum(Integer.parseInt(configItems.get(i).value));
				break;
			default:
				break;
			}
		}
		return tcpConfig;
	}

}
